package com.leosam.tvbox.mv.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leosam.tvbox.mv.data.HomeData;
import com.leosam.tvbox.mv.data.Vod;
import com.leosam.tvbox.mv.data.VodClass;
import com.leosam.tvbox.mv.data.VodResult;
import com.leosam.tvbox.mv.data.VodSeries;
import com.leosam.tvbox.mv.lucene.MvIndex;
import com.leosam.tvbox.mv.lucene.PersistentSequenceGenerator;
import com.leosam.tvbox.mv.proxy.M3u8Media;
import com.leosam.tvbox.mv.utils.StopWatch;
import com.leosam.tvbox.mv.utils.StringUtils;

import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

/**
 * @author admin
 * @since 2023/6/10 17:35
 */
public class MvService {
    private static final Logger logger = LoggerFactory.getLogger(MvService.class);
    private static final Map<String, HomeData> homeConfigMap = new LinkedHashMap<>();
    private static final String KEY_CACHED = "CACHED";
    private static final String PACKAGE_JSON = "package.json";

    public static String DATA_PATH = System.getProperty("DATA_DIR", "./data");
    public static String MEDIA_PATH = "/media";
    public static String CONFIG_PATH = "/config";
    public static String CACHE_PATH = "/media";

    private MvIndex mvIndex;

    private PersistentSequenceGenerator idGenerator;

    public VodResult searchVod(String query, int max) throws Exception {
        if (max > 10000) {
            return new VodResult().init();
        }

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        VodResult result = new VodResult().init();
        List<Vod> vods = mvIndex.search(query);
        if (vods.size() > max) {
            result.setList(vods.subList(0, max));
        } else {
            result.setList(vods);
        }
        result.setTotal(vods.size());

        stopWatch.stop();
        logger.info("search successfully, query={}, found={}, return={}, spend={}ms", query, vods.size(),
                result.getList().size(), stopWatch.getTotalTimeMillis());
        return result;
    }

    public VodResult detailVod(String[] ids, int max) throws Exception {
        if (max > 10000) {
            return new VodResult().init();
        }

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        VodResult result = new VodResult().init();

        List<Vod> vods = new ArrayList<>();
        for (String id : ids) {
            Vod vod = mvIndex.getById(id);
            if (vod != null) {
                vods.add(vod);
                if (vods.size() > max) {
                    break;
                }
            }
        }
        result.setList(vods);
        result.setTotal(vods.size());

        stopWatch.stop();
        logger.info("search successfully, return={}, spend={}ms", vods.size(), stopWatch.getTotalTimeMillis());
        return result;
    }

    public VodResult searchVodHome(String type) throws Exception {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        VodResult vodResult = new VodResult().init();
        ArrayList<VodClass> vodClassList = new ArrayList<>(homeConfigMap.size());
        for (Entry<String, HomeData> entry : homeConfigMap.entrySet()) {

            vodClassList.add(new VodClass().setTypeId(entry.getKey()).setTypeName(entry.getValue().getName()));
            if (entry.getKey().equals(type)) {
                if (entry.getKey().equals(KEY_CACHED)) {
                    vodResult.setList(mvIndex.getVodList());
                } else {
                    List<String> items = entry.getValue().getItems();
                    List<Vod> list = new ArrayList<>(items.size());
                    for (String item : items) {
                        Vod vod = mvIndex.getByName(item);
                        if (vod != null) {
                            list.add(vod);
                        }
                    }
                    vodResult.setList(list);
                }
            }
        }
        vodResult.setVodClassList(vodClassList);
        if (vodResult.getList() == null) {
            vodResult.setList(Collections.emptyList());
        }

        stopWatch.stop();
        logger.info("load home data successfully, type={}, return={}, spend={}ms", type, vodResult.getList().size(),
                stopWatch.getTotalTimeMillis());

        return vodResult;
    }

    public void cacheVod(String vodJsonString) {
        JsonObject vodJson = new JsonObject(vodJsonString);
        String vodName = MvIndex.trimVodName(vodJson.getString("name"));
        File baseFile = new File(MvService.DATA_PATH + MvService.MEDIA_PATH + "/" + vodName);
        baseFile.mkdirs();

        // 保存影片原始信息
        try (FileOutputStream fos = new FileOutputStream(new File(baseFile, "raw.json"))) {
            fos.write(vodJson.toString().getBytes("UTF-8"));
        } catch (IOException ioe) {
            logger.error("存储缓存请求报错", ioe);
            throw new RuntimeException("保存影片原始信息失败");
        }

        // 解析影片元数据
        Vod vod = parseVodInfo(vodJson, vodName);

        // 保存影片元数据
        try (FileOutputStream fos = new FileOutputStream(new File(baseFile, "package.json"))) {
            Vod.STORE_OBJ_MAPPER.writeValue(fos, vod);
        } catch (IOException ioe) {
            logger.error("存储缓存请求报错", ioe);
            throw new RuntimeException("保存影片元数据信息失败", ioe);
        }

        // 保存下载脚本
        saveDownloadScript(vodName, baseFile, vod);

        // 提交下载任务
        // for (VodSeries vodSeries : vod.getVodSeries()) {
        //     M3u8Proxy.download(vodName, vodSeries.getSeriesName());
        // }

        // 提交索引
        mvIndex.indexVod(vod);
    }

    private void saveDownloadScript(String vodName, File baseFile, Vod vod) {
        try (OutputStreamWriter fos = new OutputStreamWriter(new FileOutputStream(new File(baseFile, "run.bat")),
                Charset.forName("gbk"))) {

            fos.append("@echo off\r\n");
            for (VodSeries vodSeries : vod.getVodSeries()) {
                String seriesName = vodSeries.getSeriesName();
                String url = vodSeries.getOriginUrl();

                if (StringUtils.endsWithUrl(url, ".m3u8")) {
                    fos.append("start \"下载").append(vodName).append("-").append(seriesName)
                            .append("\" m3u8dl ").append(url).append(" --save-name \"").append(seriesName)
                            .append("\"\r\n");
                } else if (StringUtils.isMedia(url)) {
                    int lastIndexOf = url.lastIndexOf(".");
                    fos.append("start \"下载").append(vodName).append("-").append(seriesName)
                            .append("\" aria2c ").append(url).append(" -o \"").append(seriesName)
                            .append(url.substring(lastIndexOf)).append("\" --check-certificate=false\r\n");
                } else {
                    logger.warn("不支持缓存的URL:{}", url);
                }
            }
        } catch (IOException ioe) {
            logger.error("存储缓存请求报错", ioe);
            throw new RuntimeException("保存影片下载脚本失败");
        }
    }

    private Vod parseVodInfo(JsonObject vodJson, String vodName) {
        JsonArray seriesFlags = vodJson.getJsonArray("seriesFlags");
        JsonObject seriesMap = vodJson.getJsonObject("seriesMap");
        List<VodSeries> vodSeriesList = new ArrayList<>();
        Vod vod = new Vod();
        for (int i = 0; i < seriesFlags.size(); i++) {
            JsonObject seriesFlag = seriesFlags.getJsonObject(i);
            if (seriesFlag.getBoolean("selected", false)) {
                String seriesFlagName = seriesFlag.getString("name");
                JsonArray seriesList = seriesMap.getJsonArray(seriesFlagName);
                for (int j = 0; j < seriesList.size(); j++) {
                    JsonObject series = seriesList.getJsonObject(j);
                    String seriesName = series.getString("name");
                    String url = series.getString("url");
                    VodSeries vodSeries = new VodSeries();
                    vodSeries.setSeriesName(seriesName);
                    vodSeries.setOriginUrl(url);
                    vodSeries.setVod(vod);
                    if (StringUtils.endsWithUrl(url, ".m3u8")) {
                        vodSeries.setFileName(M3u8Media.NAME_INDEX_M3U8);
                    } else if (StringUtils.isMedia(url)) {
                        int lastIndexOf = url.lastIndexOf(".");
                        vodSeries.setFileName(seriesName + url.substring(lastIndexOf));
                    } else {
                        // vodSeries.setFileName(seriesName + ".mp4");
                        vodSeries.setFileName(M3u8Media.NAME_INDEX_M3U8);
                    }
                    vodSeriesList.add(vodSeries);
                }
            }
        }
        String vodId = mvIndex.getIdByName(vodName);
        if (vodId == null) {
            vodId = idGenerator.next();
        }
        vod.setVodId(vodId);
        vod.setVodActor(vodJson.getString("actor"));
        vod.setVodArea(vodJson.getString("area"));
        vod.setVodDirector(vodJson.getString("director"));
        vod.setVodName(vodName);
        vod.setVodRemarks(vodJson.getString("note"));
        vod.setVodPic(vodJson.getString("pic"));
        vod.setVodYear(vodJson.getString("year"));
        vod.setVodPlayFrom("NAS");
        vod.setCachePath("/" + vodName);
        vod.setVodSeries(vodSeriesList);
        return vod;
    }

    public synchronized void initIndex() throws Exception {
        // 加载首页配置
        buildHomeConfig();

        StopWatch stopWatch = new StopWatch();

        // 创建索引
        stopWatch.start("build indexes");
        logger.info("building indexes....");
        MvIndex mvIndex = new MvIndex();

        logger.info("↓↓↓↓↓ collecting vod ↓↓↓↓↓↓");
        File dataDir = new File(DATA_PATH + MvService.MEDIA_PATH);
        for (File dataFile : dataDir.listFiles()) {
            if (dataFile.isDirectory()) {
                File packFile = new File(dataFile, PACKAGE_JSON);
                if (packFile.exists()) {
                    try {
                        Vod vod = Vod.readValue(packFile);
                        vod.setCachePath(dataFile.getName());
                        mvIndex.indexVod(vod);
                    } catch (Exception e) {
                        logger.warn("read package.json failed: {}", packFile, e);
                    }
                }
            }
        }
        this.mvIndex = mvIndex;
        this.idGenerator = new PersistentSequenceGenerator(DATA_PATH + "/.id");
        logger.info("↑↑↑↑↑ collecting vod ↑↑↑↑↑↑");

        stopWatch.stop();
        logger.info("build indexes finish, spend={}ms", stopWatch.getTotalTimeMillis());

    }

    private void buildHomeConfig() {
        try {
            File file = new File(DATA_PATH + "/home.json");
            if (file.exists() && file.isFile()) {
                Map<String, HomeData> map = new ObjectMapper().readValue(file,
                        new TypeReference<LinkedHashMap<String, HomeData>>() {
                        });
                homeConfigMap.put(KEY_CACHED, new HomeData("已缓存"));
                homeConfigMap.putAll(map);
                logger.info("加载首页配置成功");
                return;
            }
        } catch (Exception e) {
            logger.error("buildHomeConfig error", e);
        }
    }

    public Vod findVod(String name) {
        return mvIndex.getByName(name);
    }
}
